﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Versioning;
using Cubicle.Nuget.Config;
using NuGet;

namespace Cubicle.Nuget.Core
{
    public class NugetPackageFile : INugetPackageFile
    {
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        private readonly IPackage _package;
        private readonly IPackageFile _file;

        public NugetPackageFile(IPackage package, IPackageFile file)
        {
            _package = package;
            _file = file;
            if (_file.TargetFramework == null)
                _logger.Warn("File does not have a target framework. Assuming max. Max = {0}", NugetConfig.Singleton.MaxFramework);
        }

        public IEnumerable<FrameworkName> SupportedFrameworks { get; private set; }
        public Stream GetStream()
        {
            return _file.GetStream();
        }

        public string Path { get { return _file.Path; } }
        public string EffectivePath { get { return _file.EffectivePath; } }
        public FrameworkName TargetFramework
        {
            get
            {
                return _file.TargetFramework ?? NugetConfig.Singleton.MaxFramework;
            }
        }
        public IPackage Package { get { return _package; } }

        public bool IsInBin()
        {
            return (File.Exists(BinPath) && NugetExtensions.CheckEquality(PackagePath, BinPath));
        }

        public bool IsInUse()
        {
            if (!File.Exists(BinPath)) return false;

            try
            {
                using (var _ = new FileStream(BinPath, FileMode.Open))
                {
                    return false; 
                }
            }
            catch
            {
                return true;
            }
        }

        public bool RemoveFromBin()
        {
            if (!IsInBin())
            {
                _logger.Error("No binary equivalent was found in the Bin path. Path = {0}", BinPath);
                return false;
            } 
            
            if (IsInUse())
            {
                _logger.Error("The file in the Bin path is in use by another process. Path = {0}", BinPath);
                return false;
            }

            try
            {
                _logger.Info("Removing file. Path = {0}", BinPath);
                File.Delete(BinPath);
                return true;
            }
            catch (Exception e)
            {
                _logger.ErrorException(String.Format("Error while attempting to remove file from Bin path. File = {0}", BinPath), e);
                return false;
            }
        }

        public bool CopyToBin()
        {
            if (IsInBin())
            {
                _logger.Warn("A binary equivalent was found in the Bin path, skipping file. Path = {0}", BinPath);
                return true;
            }

            if (IsInUse())
            {
                _logger.Error("A non binary-equivalent file with the same name exists in the Bin path. This file is in use by another process. Path = {0}", BinPath);
                return false;
            }

            if (File.Exists(BinPath))
            {
                _logger.Warn("A file with the same name exists in the Bin path. Overwrite = {0}", NugetConfig.Singleton.Overwrite);
                if (!NugetConfig.Singleton.Overwrite) return false;
            }

            try
            {
                _logger.Info("Copying file to Bin path. Path = {0}", BinPath);
                var directoryName = System.IO.Path.GetDirectoryName(BinPath);
                if (directoryName != null && !Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName);
                File.Copy(PackagePath, BinPath, NugetConfig.Singleton.Overwrite);
                return true;
            }
            catch (Exception e)
            {
                _logger.ErrorException(String.Format("Error while attempting to copy file to the Bin path. File = {0}", BinPath), e);
                return false;
            }
        }

        public string BinPath
        {
            get
            {
                var filePath = _file.Path.StartsWith("content") 
                    ? _file.Path.Remove(0, 8) // content files retain their directories
                    : System.IO.Path.GetFileName(_file.Path);
                return System.IO.Path.Combine(InstallPathFinder.Singleton.BinPath, filePath);
            }
        }

        public string PackagePath
        {
            get 
            {
                return System.IO.Path.Combine(InstallPathFinder.Singleton.PackagePath, _package.Id + "." + _package.Version, _file.Path);
            }
        }

    }
}